import glob
import time
import os
import numpy as np
import cv2
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
from skimage.feature import hog
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn import svm
%matplotlib inline
class feature_params():
def __init__(self):
self.color_space = 'YCrCb' # hls, hsv, yuv, ycrcb
# HOG
self.orients = 11 # 6 - 12
self.pix_per_cell = 16
self.cell_per_block = 2
self.hog_channel = 'ALL'
# Spatial Bin
self.spatial_size = (16, 16)
# Color Histogram
self.hist_bins = 32
self.hist_range = (0, 256)
def iternate_files(rpath,pattern):
images = np.array([])
for subdir, _, _ in os.walk(rpath):
subset = glob.glob(os.path.join(subdir, pattern))
if len(subset) > 0:
images = np.concatenate((images,subset))
return images
def bin_spatial(img, size=(32, 32)):
features = cv2.resize(img, size).ravel()
# print(features.shape)
return features
def color_hist(img, nbins=32, bins_range=(0, 256)):
channel1_hist = np.histogram(img[:,:,0], bins=nbins, range=bins_range)
channel2_hist = np.histogram(img[:,:,1], bins=nbins, range=bins_range)
channel3_hist = np.histogram(img[:,:,2], bins=nbins, range=bins_range)
hist_features = np.concatenate((channel1_hist[0], channel2_hist[0], channel3_hist[0]))
# print(hist_features.shape)
return hist_features
def get_hog_features(img, orient, pix_per_cell, cell_per_block, vis=False, feature_vec=True):
if vis == True:
hog_feature, hog_image = hog(img, orientations=orient, pixels_per_cell=(pix_per_cell, pix_per_cell),
cells_per_block=(cell_per_block, cell_per_block),
transform_sqrt=True,
visualise=True, feature_vector=feature_vec,
block_norm="L2-Hys")
return hog_feature, hog_image
else:
hog_feature = hog(img, orientations=orient, pixels_per_cell=(pix_per_cell, pix_per_cell),
cells_per_block=(cell_per_block, cell_per_block),
transform_sqrt=True,
visualise=False, feature_vector=feature_vec,
block_norm="L2-Hys")
return hog_feature
def extract_features(rgbimage, params):
colorspace = params.color_space
orients = params.orients
pix_per_cell = params.pix_per_cell
cell_per_block = params.cell_per_block
hog_channel = params.hog_channel
spatial_size = params.spatial_size
hist_bins = params.hist_bins
hist_range = params.hist_range
if colorspace != 'RGB':
if colorspace == 'HSV':
feature_image = cv2.cvtColor(rgbimage, cv2.COLOR_RGB2HSV)
elif colorspace == 'LUV':
feature_image = cv2.cvtColor(rgbimage, cv2.COLOR_RGB2LUV)
elif colorspace == 'HLS':
feature_image = cv2.cvtColor(rgbimage, cv2.COLOR_RGB2HLS)
elif colorspace == 'YUV':
feature_image = cv2.cvtColor(rgbimage, cv2.COLOR_RGB2YUV)
elif colorspace == 'YCrCb':
feature_image = cv2.cvtColor(rgbimage, cv2.COLOR_RGB2YCrCb)
else:
feature_image = np.copy(rgbimage)
# feature_image = (feature_image - np.min(feature_image))/(np.max(feature_image) - np.min(feature_image))
if hog_channel == 'ALL':
hog_feature = []
for channel in range(feature_image.shape[2]):
hog_feature.append(get_hog_features(feature_image[:,:,channel],
orients, pix_per_cell, cell_per_block,
vis=False, feature_vec=True))
hog_feature = np.ravel(hog_feature)
else:
hog_feature = get_hog_features( feature_image[:,:,hog_channel], orients,
pix_per_cell, cell_per_block,
vis=False, feature_vec=True)
spatial_features = bin_spatial(feature_image, size=spatial_size)
hist_features = color_hist(feature_image, nbins=hist_bins, bins_range=hist_range)
features = np.concatenate((spatial_features, hist_features, hog_feature))
return features
def show_2_images_side_by_side(img1, img2, title1='',title2='',cmap1=None,cmap2='gray'):
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(24, 9))
f.tight_layout()
if cmap1 == None:
ax1.imshow(img1)
else:
ax1.imshow(img1,cmap=cmap1)
ax1.set_title(title1, fontsize=30)
ax1.axis('off')
if cmap2 == None:
ax2.imshow(img2)
else:
ax2.imshow(img2, cmap=cmap2)
ax2.set_title(title2, fontsize=30)
ax2.axis('off')
# plt.subplots_adjust(left=0., right=1, top=0.9, bottom=0.)
def show_images(image_set, title_set=None,counts=None,cmap=None):
num_image_toshow = len(image_set) if counts is None else counts
plt.figure(1, figsize=(20, 36))
for idx in range(num_image_toshow):
plt.subplot(9,4, idx + 1)
img = image_set[idx]
if img.shape[0] >0:
if cmap is 'gray':
plt.imshow(img,cmap='gray')
else:
plt.imshow(img)
if title_set is not None:
s = '{}'.format(title_set[idx])
plt.title(s)
plt.axis('off')
plt.tight_layout()
def show_image_sets(image_files, dataset_zip, titles, col_per_row = 3):
for file_name,item in zip(image_files,dataset_zip):
img,car,heat = item
fig = plt.figure(figsize = (32, 12))
fig.subplots_adjust(left = 0, right = 1, bottom = 0, top = 1, hspace = 0.01, wspace = 0.01)
plt.subplot(1,col_per_row,1)
plt.imshow(img)
plt.title(file_name,fontsize=20)
plt.axis('off')
plt.subplot(1,col_per_row,2)
plt.imshow(car)
plt.title(titles[0],fontsize=20)
plt.axis('off')
plt.subplot(1,col_per_row,3)
plt.imshow(heat)
plt.title(titles[1],fontsize=20)
plt.axis('off')
def showHOG(img, title, params):
img_color = cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)
_, hog_y = get_hog_features(img_color[:,:,0],
params.orients, params.pix_per_cell, params.cell_per_block,
vis=True, feature_vec=True)
_, hog_Cr = get_hog_features(img_color[:,:,1],
params.orients, params.pix_per_cell, params.cell_per_block,
vis=True, feature_vec=True)
_, hog_Cb = get_hog_features(img_color[:,:,2],
params.orients, params.pix_per_cell, params.cell_per_block,
vis=True, feature_vec=True)
fig, axes = plt.subplots(ncols=4, figsize=(15,15))
axes[0].imshow(img)
axes[0].set_title(title)
axes[1].imshow(hog_y, cmap='gray')
axes[1].set_title('HOG - Y')
axes[2].imshow(hog_Cr, cmap='gray')
axes[2].set_title('HOG - Cr')
axes[3].imshow(hog_Cb, cmap='gray')
axes[3].set_title('HOG - Cb')
f_params = feature_params()
cardir = './training_data/vehicles'
noncardir = './training_data/non-vehicles'
carfiles = iternate_files(cardir,'*.png')
noncarfiles = iternate_files(noncardir,'*.png')
print('Car training images: {}'.format(len(carfiles)))
print('Non-car training images: {}'.format(len(noncarfiles)))
sample_car_file = np.random.choice(carfiles, 1, replace=False)
sample_non_car_file = np.random.choice(noncarfiles, 1, replace=False)
sample_car_image = mpimg.imread(sample_car_file[0])
sample_noncar_image = mpimg.imread(sample_non_car_file[0])
show_2_images_side_by_side(sample_car_image, sample_noncar_image,
title1='Car Sample',title2='Non-car Sample',
cmap1=None,cmap2=None)
showHOG(sample_car_image, 'Car',f_params)
showHOG(sample_noncar_image, 'Non-car',f_params)
t = time.time()
X_train_car = list(map(lambda file: extract_features(cv2.cvtColor(cv2.imread(file),cv2.COLOR_BGR2RGB),f_params),carfiles))
t2 = time.time()
X_train_noncar = list(map(lambda file: extract_features(cv2.cvtColor(cv2.imread(file),cv2.COLOR_BGR2RGB),f_params), noncarfiles))
t3 = time.time()
X = np.vstack((X_train_car, X_train_noncar)).astype(np.float64)
X_scaler = StandardScaler().fit(X)
scaled_X = X_scaler.transform(X)
y = np.hstack((np.ones(len(X_train_car)), np.zeros(len(X_train_noncar))))
rand_state = np.random.randint(0, 100)
X_train, X_test, y_train, y_test = train_test_split(scaled_X, y, test_size=0.2, random_state=rand_state)
# Fitting
t4 = time.time()
# svc = svm.LinearSVC()
# svc.fit(X_train, y_train)
# svm_parameters = {'C':[0.0001, 0.001, 0.1, 1], 'gamma': [0.001, 0.01, 0.1,1]}
# svc = svm.SVC(kernel='rbf')
svm_parameters = {'kernel':('linear', 'rbf'), 'C':[0.01, 0.1, 1, 10]}
svc = svm.SVC()
clf = GridSearchCV(svc, svm_parameters, verbose=2, cv=3)
clf.fit(X_train, y_train)
print('Best parameters:')
print(clf.best_params_)
t5 = time.time()
# accuracy = round(svc.score(X_test, y_test), 4)
accuracy = round(clf.score(X_test, y_test), 4)
print('Training time: {0}'.format(round(t5 - t4, 2)))
print('Test accuracy: {0}'.format(accuracy))
svc_best = svm.SVC(kernel='rbf', C=10)
svc_best.fit(X_train, y_train)
acc_best = round(svc_best.score(X_test, y_test), 4)
print(acc_best)
def slide_window(img, x_start_stop=[None, None], y_start_stop=[None, None],
xy_window=(64, 64), xy_overlap=(0.5, 0.5)):
# If x and/or y start/stop positions not defined, set to image size
if x_start_stop[0] == None:
x_start_stop[0] = 0
if x_start_stop[1] == None:
x_start_stop[1] = img.shape[1]
if y_start_stop[0] == None:
y_start_stop[0] = 0
if y_start_stop[1] == None:
y_start_stop[1] = img.shape[0]
# Compute the span of the region to be searched
xspan = x_start_stop[1] - x_start_stop[0]
yspan = y_start_stop[1] - y_start_stop[0]
# Compute the number of pixels per step in x/y
nx_pix_per_step = np.int(xy_window[0]*(1 - xy_overlap[0]))
ny_pix_per_step = np.int(xy_window[1]*(1 - xy_overlap[1]))
# Compute the number of windows in x/y
nx_buffer = np.int(xy_window[0]*(xy_overlap[0]))
ny_buffer = np.int(xy_window[1]*(xy_overlap[1]))
nx_windows = np.int((xspan-nx_buffer)/nx_pix_per_step)
ny_windows = np.int((yspan-ny_buffer)/ny_pix_per_step)
# Initialize a list to append window positions to
window_list = []
# Loop through finding x and y window positions
# Note: you could vectorize this step, but in practice
# you'll be considering windows one by one with your
# classifier, so looping makes sense
for ys in range(ny_windows):
for xs in range(nx_windows):
# Calculate window position
startx = xs*nx_pix_per_step + x_start_stop[0]
endx = startx + xy_window[0]
starty = ys*ny_pix_per_step + y_start_stop[0]
endy = starty + xy_window[1]
# Append window position to list
window_list.append(((startx, starty), (endx, endy)))
# Return the list of windows
return window_list
def draw_boxes(img, bboxes, color=(0, 255, 0), thick=2):
# Make a copy of the image
imcopy = np.copy(img)
# Iterate through the bounding boxes
for bbox in bboxes:
# Draw a rectangle given bbox coordinates
cv2.rectangle(imcopy, bbox[0], bbox[1], color, thick)
# Return the image copy with boxes drawn
return imcopy
def pyramid(image, downscale=1.5, minSize=(30, 30)):
yield image
while True:
w = int(image.shape[1] / downscale)
h = int(image.shape[0] / downscale)
image = cv2.resize(image, (w,h))
if image.shape[0] < minSize[1] or image.shape[1] < minSize[0]:
break
yield image
# Heat map and threshold functions from Udacity's course
def add_heat(heatmap, bbox_list):
# Iterate through list of bboxes
for box in bbox_list:
# Add += 1 for all pixels inside each bbox
# Assuming each "box" takes the form ((x1, y1), (x2, y2))
heatmap[box[0][1]:box[1][1], box[0][0]:box[1][0]] += 1
# Return updated heatmap
return heatmap
def apply_threshold(heatmap, threshold):
# Zero out pixels below the threshold
heatmap[heatmap <= threshold] = 0
# Return thresholded map
return heatmap
def draw_labeled_bboxes(img, labels):
# Iterate through all detected cars
for car_number in range(1, labels[1]+1):
# Find pixels with each car_number label value
nonzero = (labels[0] == car_number).nonzero()
# Identify x and y values of those pixels
nonzeroy = np.array(nonzero[0])
nonzerox = np.array(nonzero[1])
# Define a bounding box based on min/max x and y
bbox = ((np.min(nonzerox), np.min(nonzeroy)), (np.max(nonzerox), np.max(nonzeroy)))
# Draw the box on the image
cv2.rectangle(img, bbox[0], bbox[1], (0,255,0), 6)
# Return the image
return img
def apply_heatmap(img, boxes,th=4):
heat = np.zeros_like(img[:,:,0]).astype(np.float)
heatmap = add_heat(heat, boxes)
heatmap = apply_threshold(heatmap, threshold=th)
return heatmap
all_test_images_set = glob.glob('./test_images/*.jpg')
org_image_set = list(map(lambda f: cv2.cvtColor(cv2.imread(f),cv2.COLOR_BGR2RGB), all_test_images_set))
from scipy.ndimage.measurements import label
car_image_set = []
heat_map_set = []
for t_img_file in all_test_images_set:
t_img = cv2.cvtColor(cv2.imread(t_img_file),cv2.COLOR_BGR2RGB)
ty = 380
by = 656
lx = 0
rx = 1280
slide_windows = slide_window(t_img,x_start_stop=[lx,rx], y_start_stop=[ty,by],
xy_window=[64,64], xy_overlap=[0.85,0.85])
car_windows = []
for win in slide_windows:
sub_img = cv2.resize(t_img[win[0][1]:win[1][1], win[0][0]:win[1][0]], (64, 64))
all_features = extract_features(sub_img, f_params)
scaled_all_features = X_scaler.transform(all_features.reshape(1, -1))
pred = svc_best.predict(scaled_all_features)
if pred == 1:
car_windows.append(win)
recopy = np.copy(t_img)
heatmap = apply_heatmap(recopy,car_windows,th=4)
heat_map_set.append(heatmap)
labels = label(heatmap)
recopy = draw_labeled_bboxes(recopy, labels)
car_image_set.append(recopy)
show_images(car_image_set,all_test_images_set)
for img, heat in zip(car_image_set,heat_map_set):
show_2_images_side_by_side(img, heat,
title1='Image',title2='Heat',
cmap1=None,cmap2=None)
titles = ['Car','Heat']
show_image_sets(all_test_images_set, zip(org_image_set,car_image_set,heat_map_set),titles)
def find_car_boxes(img, y_start_stop=[350, 656], window=64, cells_per_step=1, scale=1.5 ):
# Parameters extraction
# HOG parameters
cspace = f_params.color_space
orient = f_params.orients
pix_per_cell = f_params.pix_per_cell
cell_per_block = f_params.cell_per_block
hog_channel = f_params.hog_channel
# Spatial parameters
size = f_params.spatial_size
# Histogram parameters
hist_bins = f_params.hist_bins
hist_range = f_params.hist_range
# Image color space changes
# apply color conversion if other than 'RGB'
if cspace != 'RGB':
if cspace == 'HSV':
feature_image = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
elif cspace == 'LUV':
feature_image = cv2.cvtColor(img, cv2.COLOR_RGB2LUV)
elif cspace == 'HLS':
feature_image = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)
elif cspace == 'YUV':
feature_image = cv2.cvtColor(img, cv2.COLOR_RGB2YUV)
elif cspace == 'YCrCb':
feature_image = cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)
else: feature_image = np.copy(img)
ystart, ystop = y_start_stop
ctrans_tosearch = feature_image[ystart:ystop,:,:]
if scale != 1:
imshape = ctrans_tosearch.shape
ctrans_tosearch = cv2.resize(ctrans_tosearch, (np.int(imshape[1]/scale), np.int(imshape[0]/scale)))
ch1 = ctrans_tosearch[:,:,0]
ch2 = ctrans_tosearch[:,:,1]
ch3 = ctrans_tosearch[:,:,2]
# Define blocks and steps as above
nxblocks = (ch1.shape[1] // pix_per_cell) - cell_per_block + 1
nyblocks = (ch1.shape[0] // pix_per_cell) - cell_per_block + 1
nfeat_per_block = orient*cell_per_block**2
nblocks_per_window = (window // pix_per_cell) - cell_per_block + 1
nxsteps = (nxblocks - nblocks_per_window) // cells_per_step
nysteps = (nyblocks - nblocks_per_window) // cells_per_step
# Compute individual channel HOG features for the entire image
hog1 = get_hog_features(ch1, orient, pix_per_cell, cell_per_block, feature_vec=False)
hog2 = get_hog_features(ch2, orient, pix_per_cell, cell_per_block, feature_vec=False)
hog3 = get_hog_features(ch3, orient, pix_per_cell, cell_per_block, feature_vec=False)
car_windows = []
for xb in range(nxsteps):
for yb in range(nysteps):
ypos = yb*cells_per_step
xpos = xb*cells_per_step
# Extract HOG for this patch
hog_feat1 = hog1[ypos:ypos+nblocks_per_window, xpos:xpos+nblocks_per_window].ravel()
hog_feat2 = hog2[ypos:ypos+nblocks_per_window, xpos:xpos+nblocks_per_window].ravel()
hog_feat3 = hog3[ypos:ypos+nblocks_per_window, xpos:xpos+nblocks_per_window].ravel()
hog_features = np.hstack((hog_feat1, hog_feat2, hog_feat3))
xleft = xpos*pix_per_cell
ytop = ypos*pix_per_cell
# Extract the image patch
subimg = cv2.resize(ctrans_tosearch[ytop:ytop+window, xleft:xleft+window], (64,64))
# Get color features
spatial_features = bin_spatial(subimg, size=size)
hist_features = color_hist(subimg, nbins=hist_bins, bins_range=hist_range)
# Scale features and make a prediction
test_features = X_scaler.transform(np.hstack((spatial_features, hist_features, hog_features)).reshape(1, -1))
test_prediction = svc_best.predict(test_features)
if test_prediction == 1:
xbox_left = np.int(xleft*scale)
ytop_draw = np.int(ytop*scale)
win_draw = np.int(window*scale)
car_windows.append(((xbox_left, ytop_draw+ystart),(xbox_left+win_draw,ytop_draw+win_draw+ystart)))
return car_windows
from scipy.ndimage.measurements import label
car_image_set_fast = []
heatmap_set_fast = []
for t_img_file in all_test_images_set:
t_img = cv2.cvtColor(cv2.imread(t_img_file),cv2.COLOR_BGR2RGB)
car_boxes_fast = find_car_boxes(t_img)
car_img = np.copy(t_img)
# car_img = draw_boxes(car_img,car_boxes_fast)
# Apply heatmap
heatmap = apply_heatmap(car_img,car_boxes_fast,th=1)
heatmap_set_fast.append(heatmap)
labels = label(heatmap)
car_img = draw_labeled_bboxes(car_img, labels)
car_image_set_fast.append(car_img)
show_images(car_image_set_fast,all_test_images_set)
for img, heat in zip(car_image_set_fast,heatmap_set_fast):
show_2_images_side_by_side(img, heat,
title1='Image',title2='Heat',
cmap1=None,cmap2=None)
def find_car_window_sliding_window(img, y_ss=[380, 656]):
slide_windows = slide_window(img,x_start_stop=[0,1280], y_start_stop=y_ss,
xy_window=[64,64], xy_overlap=[0.85,0.85])
car_windows = []
for win in slide_windows:
sub_img = cv2.resize(img[win[0][1]:win[1][1], win[0][0]:win[1][0]], (64, 64))
all_features = extract_features(sub_img, f_params)
scaled_all_features = X_scaler.transform(all_features.reshape(1, -1))
pred = svc_best.predict(scaled_all_features)
if pred == 1:
car_windows.append(win)
return car_windows
from scipy.ndimage.measurements import label
from moviepy.editor import VideoFileClip
class HeatHistory():
def __init__(self):
self.history = []
self.acc = None
def processVideo(inputVideo, outputVideo, frames_to_remember=3, thd=1):
history = HeatHistory()
def pipeline(img):
boxes = find_car_boxes(img)
# boxes = find_car_window_sliding_window(img)
img_shape = img.shape
heat = np.zeros_like(img[:,:,0]).astype(np.float)
heatmap = add_heat(heat, boxes)
if history.acc is None:
history.acc = np.zeros_like(heat)
if len(history.history) >= frames_to_remember:
history.history = history.history[1:]
history.acc = history.acc - history.history[0]
history.history.append(heatmap)
history.acc = history.acc + heatmap
if len(history.history) >= frames_to_remember:
heatmap = history.acc / frames_to_remember
heatmap = apply_threshold(heatmap, threshold=thd)
labels = label(heatmap)
return draw_labeled_bboxes(np.copy(img), labels)
myclip = VideoFileClip(inputVideo)
output_video = myclip.fl_image(pipeline)
output_video.write_videofile(outputVideo, audio=False)
processVideo('./project_video.mp4', './project_video_output.mp4', thd=1)
# processVideo('./test_video.mp4', './test_video_output.mp4', thd=1)
from IPython.display import HTML
HTML("""
<video width="960" height="540" controls>
<source src="{0}">
</video>
""".format('project_video_output.mp4'))